//Jeffery Zhu
//Rui Liu
//CS380C
//lab1 

#include "parser.h"


void Param::set(ParamType t, int val, string str) {
	type = t;
	value = val;
	literal = str;
}

void Param::parse(string s) {
	int val;
	int i;
	if (s.empty())
		set(NA);
	else if (s.compare("GP") == 0)
		set(GP);
	else if (s.compare("FP") == 0)
		set(FP);
	else if (isNum(s))
		set(CONST, atoi(s.c_str()));
	else if (s[0] == '(' && s[s.size() - 1] == ')')
		set(REG, atoi(s.substr(1, s.size() - 2).c_str()));
	else if (s[0] == '[' && s[s.size() - 1] == ']')
		set(LABEL, atoi(s.substr(1, s.size() - 2).c_str()));
	else if ((i = s.find('#')) != string::npos) {
		string left = s.substr(0, i);
		string right = s.substr(i + 1, s.size() - i - 1);
		int j;
		val = atoi(right.c_str());
		if ((j = left.find_last_of('_')) != string::npos && left.substr(j, left.size() - j).compare("_base") == 0)
			set(ADDR_OFFSET, val, left.substr(0, left.size() - 5));
		else if ((j = left.find_last_of('_')) != string::npos
			&& left.substr(j, left.size() - j).compare("_offset") == 0)
			set(FIELD_OFFSET, val, left.substr(0, left.size() - 7));
		else
			set(LOCAL_VAR, val, left);
	} else
		set(NA);
}

void Param::print(ostream& out){
	switch(type){
		case NA: break;
		case GP: out<<" GP"; break;
		case FP: out<<" FP"; break;
		case CONST: out<< " " << value; break;
		case ADDR_OFFSET: out<<" " << literal << "_base#" << value; break;
		case FIELD_OFFSET: out<< " " << literal << "_offset#" << value; break;
		case LOCAL_VAR: out<< " " << literal << "#" << value ; break;
		case REG: out<< " (" << value << ")"; break;
		case LABEL: out<< " [" << value << "]"; break;
	}
}


bool Param::isNum(string s) {
	if (s.empty() || (s.at(0) != '-' && !isdigit(s.at(0))))
		return false;

	for (int i = 1; i < s.size(); i++) {
		if (!isdigit(s.at(i)))
			return false;
	}
	return true;
}


void Instr::parse(string s) {
	stringstream ss(s);
	string o, p1, p2;
	ss >> o;
	ss >> o;
	ss >> o;
	ss >> p1;
	ss >> p2;
	this->sOpt = o;
	this->opt = parseOpt(o);
	this->param1.parse(p1);
	this->param2.parse(p2);
}

Operator Instr::parseOpt(string o) {
	if (o.compare("nop") == 0)
		return NOP;
	else if (o.compare("add") == 0)
		return ADD;
	else if (o.compare("sub") == 0)
		return SUB;
	else if (o.compare("mul") == 0)
		return MUL;
	else if (o.compare("div") == 0)
		return DIV;
	else if (o.compare("mod") == 0)
		return MOD;
	else if (o.compare("neg") == 0)
		return NEG;
	else if (o.compare("cmpeq") == 0)
		return CMPEQ;
	else if (o.compare("cmple") == 0)
		return CMPLE;
	else if (o.compare("cmplt") == 0)
		return CMPLT;
	else if (o.compare("br") == 0)
		return BR;
	else if (o.compare("blbc") == 0)
		return BLBC;
	else if (o.compare("blbs") == 0)
		return BLBS;
	else if (o.compare("call") == 0)
		return CALL;
	else if (o.compare("load") == 0)
		return LOAD;
	else if (o.compare("store") == 0)
		return STORE;
	else if (o.compare("move") == 0)
		return MOVE;
	else if (o.compare("read") == 0)
		return READ;
	else if (o.compare("write") == 0)
		return WRITE;
	else if (o.compare("wrl") == 0)
		return WRL;
	else if (o.compare("param") == 0)
		return PARAM;
	else if (o.compare("enter") == 0)
		return ENTER;
	else if (o.compare("ret") == 0)
		return RET;
	else if (o.compare("entrypc") == 0)
		return ENTRYPC;
	else
		return UNDEF;
}

void Instr::print(ostream& out) {
	out<<sOpt;
	param1.print(out);
	param2.print(out);
	out<<endl;

}

void IR::print(ostream& out){
	for(int i = 1; i < instructions.size(); i++){
		out<<"instr " << i <<": ";
		instructions[i].print(out);
	}
}

void IR::refine(){
	int c = 1;
	vector<Instr> orig = instructions;

	for(int i = 1; i < orig.size(); i++){
		if(orig[i].opt != UNDEF){
			if(c != i){
				for(int j = 1; j< orig.size(); j++){
					Instr& instr = orig[j];
					if((instr.param1.type == REG || instr.param1.type == LABEL) && instr.param1.value == i)
						instr.param1.value = c;
					if((instr.param2.type == REG || instr.param2.type == LABEL) && instr.param2.value == i)
						instr.param2.value = c;
				}
			}
			c++;
		}
	}

	instructions.clear();
	for(int i = 0; i < orig.size(); i++){
		if(orig[i].opt != UNDEF){
			instructions.push_back(orig[i]);
		}
	}
}

